package com.sugon.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.cait.service.rescenter.UserInfo;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.sugon.annotation.AutoLog;
import com.sugon.consts.CommonConstant;
import com.sugon.modules.sys.service.IResCenterAuthenticator;
import com.sugon.service.BaseCommonService;
import com.sugon.service.SysConfigService;
import com.sugon.utils.Constant;
import com.sugon.utils.R;
import com.sugon.utils.ShiroUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 登录相关
 *
 * @author lipengjun
 * @date 2017年11月19日 上午9:49:19
 * @since 2020-06-16 11:08
 * 1、增加异常信息日志
 * 2、增加用户登录失败次数处理
 */
@Slf4j
@Controller
public class SysLoginController {
    @Autowired
    private Producer producer;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SysConfigService configService;
    @Autowired
    private IResCenterAuthenticator resCenterAuthenticator;
    @Resource
    private BaseCommonService baseCommonService;

    @RequestMapping("captcha.jpg")
    public void captcha(HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");
        //生成文字验证码
        String text = producer.createText();
        //生成图片验证码
        BufferedImage image = producer.createImage(text);
        //保存到shiro session
        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "jpg", out);
    }

    @ResponseBody
    @RequestMapping(value = "/sys/login", method = RequestMethod.POST)
    public R login(@RequestParam("username") String username, @RequestParam("password") String password, String captcha) {
        // 校验密码中不含有特殊字符
        //if (StrUtil.containsAny(password, "+", "&", "%", "=", "*", "_")) {
        //    return R.error("请确保密码不含有 +&%=*_ 这5个特殊字符后进行登录！");
        //}
        // 先获取登录失败次数：如果超过5次需要进行验证码校验
        long loginAttemptTime = redisTemplate.opsForValue().increment(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username, 1);
        if (loginAttemptTime > 6) {
            if (StrUtil.isEmpty(captcha)) {
                return R.error("验证码必填！").put("incorrectTimes", loginAttemptTime);
            }
            // 校验验证码
            String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
            if (StrUtil.isEmpty(kaptcha)) {
                return R.error("验证码已失效，请刷新验证码后重新输入！").put("incorrectTimes", loginAttemptTime);
            } else if (!captcha.equalsIgnoreCase(kaptcha)) {
                return R.error("验证码不正确，请确认！").put("incorrectTimes", loginAttemptTime);
            }
        }
        // 校验登陆失败次数是否已超过系统设置次数
        int maxLoginAttemptTime = configService.getMaxLoginAttemptTime();
        if (loginAttemptTime > maxLoginAttemptTime) {
            return R.error("登陆失败次数已超" + maxLoginAttemptTime + "次,请一小时后再试").put("incorrectTimes", loginAttemptTime);
        }
        try {
            Subject subject = ShiroUtils.getSubject();
            // 需要配合OA统一认证,改为Base64加密 by Yu
            password = Base64.encode(password);
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            subject.login(token);
        } catch (UnknownAccountException e) {
            log.error("用户不存在", e);
            return R.error("用户名或密码错误！").put("incorrectTimes", loginAttemptTime);
        } catch (IncorrectCredentialsException e) {
            redisTemplate.expire(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username, 1, TimeUnit.HOURS);
            log.error("密码错误", e);
            return R.error("用户名或密码错误！").put("incorrectTimes", loginAttemptTime);
        } catch (LockedAccountException e) {
            log.error("用户已锁定", e);
            return R.error(e.getMessage()).put("incorrectTimes", loginAttemptTime);
        } catch (AuthenticationException e) {
            log.error("账户验证失败", e);
            return R.error("账户验证失败").put("incorrectTimes", loginAttemptTime);
        }
        // 添加登录成功日志
        baseCommonService.addLog("用户登录", "用户名: " + username + ",登录成功[PC端]！", CommonConstant.LOG_TYPE_1, null, CommonConstant.OPERATE_CLIENT_PC);
        // 如果登陆成功，则移除记录
        redisTemplate.delete(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username);
        return R.ok();
    }

    /**
     * 单点登录
     */
    @AutoLog("单点登录")
    @GetMapping("/sso")
    public String singleSignOn(@RequestParam("IdentityToken") String identityToken) {
        if (StrUtil.isEmpty(identityToken)) {
            return "forward:/login.html";
        }
        UserInfo currentUser = resCenterAuthenticator.getUserByToken(identityToken);
        if (currentUser == null) {
            throw new UnknownAccountException(StrUtil.format("无效IdentityToken，获取用户信息失败！", identityToken));
        }

        String username = currentUser.getLoginID();
        // 校验登陆失败次数是否已超过系统设置次数
        Long loginAttemptTime = redisTemplate.opsForValue().increment(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username, 1);
        int maxLoginAttemptTime = configService.getMaxLoginAttemptTime();
        if (NumberUtil.parseInt(loginAttemptTime.toString()) > maxLoginAttemptTime) {
            throw new UnknownAccountException(StrUtil.format("登陆失败次数已超{}次,请一小时后再试", identityToken));
        }
        // 单点登录无需校验密码
        Subject subject = ShiroUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, "");
        subject.login(token);
        // 如果登陆成功，则移除记录
        redisTemplate.delete(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username);

        // 添加登录成功日志
        baseCommonService.addLog("用户登录", "用户名: " + username + ",登录成功[第三方登录]！", CommonConstant.LOG_TYPE_1, null, CommonConstant.OPERATE_CLIENT_PC);
        return "redirect:/index.html";
    }

    /**
     * ERMS2配置跳转
     *
     * @param module        模块名称
     * @param subModule     子模块名称
     * @param identityToken 用户身份令牌
     * @return
     */
    @AutoLog("单点登录")
    @GetMapping("/ermsSSO/{module}/{subModule}")
    public String singleSignOn(@PathVariable("module") String module, @PathVariable("subModule") String subModule, @RequestParam("IdentityToken") String identityToken, HttpServletRequest request) {
        if (StrUtil.isEmpty(identityToken)) {
            return "forward:/login.html";
        }
        UserInfo currentUser = resCenterAuthenticator.getUserByToken(identityToken);
        if (currentUser == null) {
            throw new UnknownAccountException(StrUtil.format("无效IdentityToken，获取用户信息失败！", identityToken));
        }
        String username = currentUser.getLoginID();
        // 校验登陆失败次数是否已超过系统设置次数
        Long loginAttemptTime = redisTemplate.opsForValue().increment(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username, 1);
        int maxLoginAttemptTime = configService.getMaxLoginAttemptTime();
        if (NumberUtil.parseInt(loginAttemptTime.toString()) > maxLoginAttemptTime) {
            throw new UnknownAccountException(StrUtil.format("登陆失败次数已超{}次,请一小时后再试", identityToken));
        }
        // 单点登录无需校验密码
        Subject subject = ShiroUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, "");
        subject.login(token);
        // 如果登陆成功，则移除记录
        redisTemplate.delete(Constant.USER_LOGIN_ATTEMPT_TIME_PREFIX + username);
        // subModule存在有和没有两种情况
        return StrUtil.format("redirect:/{}/{}.html?{}", module, subModule, request.getQueryString());
    }

    /**
     * 退出
     */
    @RequestMapping(value = "logout", method = RequestMethod.GET)
    public String logout() {
        // 用户退出登陆
        ShiroUtils.logout();
        return "redirect:/";
    }

}
